package cn.lastjune.util;

import java.util.Collections;
import java.util.List;

import org.apache.commons.lang.StringUtils;

public class Page<T> {

	public static final String ASC = "asc";
	public static final String DESC = "desc";

	// -- Pagination Parameters --//
	protected int pageNo = 1;
	protected int pageSize = 11;
	protected String orderBy = null;
	protected String order = null;
	protected boolean autoCount = true;

	// -- Result --//
	protected List<T> result = Collections.emptyList();
	protected long totalCount = -1;

	public Page() {
	}

	public Page(int pageSize) {
		this.pageSize = pageSize;
	}

	public int getPageNo() {
		return pageNo;
	}

	public void setPageNo(final int pageNo) {
		this.pageNo = pageNo < 1 ? 1 : pageNo;
	}

	public Page<T> pageNo(final int thePageNo) {
		setPageNo(thePageNo);
		return this;
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(final int pageSize) {
		this.pageSize = pageSize < 1 ? 1 : pageSize;
	}

	public Page<T> pageSize(final int thePageSize) {
		setPageSize(thePageSize);
		return this;
	}

	/**
	 * Calculate the first record's position in the whole dataset according to pageNo and pageSize
	 */
	public int getFirst() {
		return (pageNo - 1) * pageSize + 1;
	}

	/**
	 * Get "order by" columns, separated by "," if there exists more than one column
	 */
	public String getOrderBy() {
		return orderBy;
	}

	/**
	 * Set "order by" columns, separated by "," if there exists more than one column
	 */
	public void setOrderBy(final String orderBy) {
		this.orderBy = orderBy;
	}

	public Page<T> orderBy(final String theOrderBy) {
		setOrderBy(theOrderBy);
		return this;
	}

	/**
	 * Get "order by" direction
	 */
	public String getOrder() {
		return order;
	}

	/**
	 * Set "order by" direction
	 * 
	 * @param order optional values are "asc" or "desc", separated by "," if there exists more than one
	 */
	public void setOrder(final String order) {
		String[] orders = StringUtils.split(StringUtils.lowerCase(order), ',');
		for (String orderStr : orders) {
			if (!StringUtils.equals(DESC, orderStr) && !StringUtils.equals(ASC, orderStr)) {
				throw new IllegalArgumentException("Order direction " + orderStr + " is illegal");
			}
		}
		this.order = StringUtils.lowerCase(order);
	}

	public Page<T> order(final String theOrder) {
		setOrder(theOrder);
		return this;
	}

	public boolean isOrderBySetted() {
		return StringUtils.isNotBlank(orderBy) && StringUtils.isNotBlank(order);
	}

	public boolean isAutoCount() {
		return autoCount;
	}

	public void setAutoCount(final boolean autoCount) {
		this.autoCount = autoCount;
	}

	public Page<T> autoCount(final boolean theAutoCount) {
		setAutoCount(theAutoCount);
		return this;
	}

	// -- Query Result Functions --//

	/**
	 * Get result
	 */
	public List<T> getResult() {
		return result;
	}

	/**
	 * Set result
	 */
	public void setResult(final List<T> result) {
		this.result = result;
	}

	public long getTotalCount() {
		return totalCount;
	}

	public void setTotalCount(final long totalCount) {
		this.totalCount = totalCount;
	}

	/**
	 * Calculate totalPage accordingto pageSize and totalCount
	 */
	public long getTotalPages() {
		if (totalCount < 0) {
			return -1;
		}

		long count = totalCount / pageSize;
		if (totalCount % pageSize > 0) {
			count++;
		}
		return count;
	}

	/**
	 * Whether exists next page?
	 */
	public boolean isHasNext() {
		return pageNo + 1 <= getTotalPages();
	}

	/**
	 * Return next pageNo, if current page is the last one, return current page no
	 */
	public int getNextPage() {
		return isHasNext() ? pageNo + 1 : pageNo;
	}

	/**
	 * Whether exists last page?
	 */
	public boolean isHasPre() {
		return pageNo - 1 >= 1;
	}

	/**
	 * Return last pageNo, if current page is the first one, return current page no
	 */
	public int getPrePage() {
		return isHasPre() ? pageNo - 1 : pageNo;
	}
}
